#include "xen/renderer/shader.hpp"

#include "platform/opengl/opengl_shader.hpp"
#include "xen/core.hpp"
#include "xen/renderer/renderer.hpp"

namespace Xen {

Ref<Shader> Shader::CreateFromFile(const std::string& name, const std::string& path) {
    std::ifstream file(path);

    XEN_CORE_ASSERT(!file.fail(), path + " can't be read");

    std::string vertex_source, fragment_source;
    std::string line;

    int shader_type = 0; // 0-none 1-vertex 2-fragment
    while (!file.eof()) {
        std::getline(file, line);
        if (line.find("#type") != std::string::npos) {
            if (line.find("vertex") != std::string::npos) {
                shader_type = 1;
                std::getline(file, line);
            } else if (line.find("fragment") != std::string::npos) {
                shader_type = 2;
                std::getline(file, line);
            } else {
                XEN_CORE_ASSERT(false, "Unknown glsl type (check `#version` in glsl file)");
            }
        }

        switch (shader_type) {
            case 1:
                vertex_source += line + "\r\n";
                break;
            case 2:
                fragment_source += line + "\r\n";
                break;
        }
    }

    file.close();

    return Shader::CreateFromSource(name, vertex_source, fragment_source);
}

Ref<Shader> Shader::CreateFromSource(const std::string& name, const std::string& vertex_source, const std::string& fragment_source) {
    switch (Renderer::GetAPI()) {
        case RendererAPI::API::OpenGL: return std::make_shared<OpenGLShader>(name, vertex_source, fragment_source);
    }
    XEN_CORE_ASSERT(false, "Unknown API type");
    return nullptr;
}

void ShaderLibrary::Add(const Ref<Shader> &shader) {
    auto& name = shader->GetName();
    XEN_CORE_ASSERT(!Exists(name), name + " already exists");
    shader_map_[shader->GetName()] = shader;
}

Ref<Shader> ShaderLibrary::Load(const std::string& name, const std::string &path) {
    auto shader = Shader::CreateFromFile(name, path);
    Add(shader);
    return shader;
}

Ref<Shader> ShaderLibrary::Get(const std::string &name) {
    XEN_CORE_ASSERT(Exists(name), name + " shader not found");
    return shader_map_[name];
}

bool ShaderLibrary::Exists(const std::string& name) const {
    return shader_map_.find(name) != shader_map_.end();
}

}
